﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace Zeus.Mvc.Loggers.Implementations
{
    public abstract class DefaultHttpContextLogger : IHttpContextLogger
    {
        protected HttpContextLogModel _httpContextModel;

        private Stream _originRequestBody;
        private StreamReader _requestBodyReader;
        protected Stream _originResponseBody;
        private StreamReader _responseBodyReader;
        private Stopwatch stopwatch;
        public DefaultHttpContextLogger()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
        }

        protected virtual async Task<IHttpContextModel> BuildRequestModel(HttpContext httpContext)
        {

            _requestBodyReader = new StreamReader(httpContext.Request.Body);
            _httpContextModel = new HttpContextLogModel()
            {
                Host = httpContext.Request.Host.ToString(),
                GuidRequest = httpContext.HttpCorrelationId(),
                RemoteAddress = httpContext.RemoteAddress(),
                ForwardedAddress = httpContext.ForwardedAddress(),
                Uri = UriHelper.GetDisplayUrl(httpContext.Request),
                RequestBody = await _requestBodyReader.ReadToEndAsync(),
                TimeRequest = DateTime.Now,
                Headers = BuildHeader(httpContext.Request.Headers),
            };
            httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
            return _httpContextModel;

        }

        protected virtual async Task<IHttpContextModel> BuildResponseModel(HttpContext httpContext)
        {
            httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
            _responseBodyReader = new StreamReader(httpContext.Response.Body);
            _httpContextModel.ResponseBody = await _responseBodyReader.ReadToEndAsync();
            _httpContextModel.TimeResponse = DateTime.Now;
            stopwatch.Stop();
            _httpContextModel.ElapsedMilliseconds = stopwatch.ElapsedMilliseconds;
            return _httpContextModel;
        }

        protected virtual Task<IHttpContextModel> BuildExceptionModel(HttpContext httpContext, Exception ex)
        {
            if (_httpContextModel == null)
            {
                _httpContextModel = new HttpContextLogModel();
            }
            _httpContextModel.ExceptionInfo = ex;
            return Task.FromResult<IHttpContextModel>(_httpContextModel);
        }

        public virtual async Task RequestLog(HttpContext httpContext)
        {
            _originRequestBody = httpContext.Request.Body;
            _originResponseBody = httpContext.Response.Body;
            var msRquestBody = new MemoryStream();
            await httpContext.Request.Body.CopyToAsync(msRquestBody);
            msRquestBody.Seek(0, SeekOrigin.Begin);
            httpContext.Request.Body = msRquestBody;
            httpContext.Response.Body = new MemoryStream();
            await BuildRequestModel(httpContext);
        }

        public virtual async Task ResponseLog(HttpContext httpContext)
        {
            await BuildResponseModel(httpContext);
            httpContext.Request.Body = _originRequestBody;
            httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
            await httpContext.Response.Body.CopyToAsync(_originResponseBody);
            httpContext.Response.Body = _originResponseBody;
            _requestBodyReader.Close();
            _responseBodyReader.Close();
        }

        public virtual async Task ExceptionLog(HttpContext context, Exception ex)
        {
            context.Request.Body = _originRequestBody ?? context.Request.Body;
            context.Response.Body = _originResponseBody ?? context.Response.Body;

            _requestBodyReader?.Close();
            _responseBodyReader?.Close();
            await BuildExceptionModel(context, ex);
        }

        public virtual string BuildHeader(IHeaderDictionary keyValuePairs)
        {
            var stringBuilder = new StringBuilder();
            foreach (var item in keyValuePairs)
            {
                stringBuilder.Append(item.Key + "=" + item.Value + ";");
            }
            return stringBuilder.ToString();

        }

        public void Dispose()
        {
            _httpContextModel = null;
            _requestBodyReader?.Dispose();
            _responseBodyReader?.Dispose();
        }
    }
}
